home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 31 / Amiga Format CD31 (1998-09-02)(Future Publishing)(GB)(Track 1 of 2)[!][issue 1998-10].iso / -seriously_amiga- / sound / ptmid / ptmid.c < prev    next >
C/C++ Source or Header  |  1998-07-20  |  14KB  |  422 lines

  1. /*
  2.  * ptmid.c: Creates Protracker MODule files from MIDI files.
  3.  * (My first attempt at Hungarian Notation.. wince!)
  4.  *
  5.  * Author: Andrew Scott  (c)opyright 1994
  6.  *
  7.  * Date: 17/11/1993 ver 0.0
  8.  *       8/1/1994   ver 0.1
  9.  */
  10.  
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <ctype.h>
  15. /* #include <io.h> */  /* BJS */
  16. #include "ptmid.h"
  17.  
  18. char bDrumch = 9,
  19.      szId[5] = "M.K.";
  20. int  fNocopy = 0,
  21.      fQuiet = 0,
  22.      fExtend = 0;
  23. int  wPatmax = 64,
  24.      wPtchan = 4,
  25.      wQuantval = 16;
  26. SI   *rgpsiDrum[128],
  27.      **rgppsiIns[129],
  28.      *psiTree = NULL;
  29. Sz   szTitle = "Converted by PTMID!",
  30.      szQuant = NULL;
  31.  
  32. /*
  33.  * Init: Does all those initialization things (which aren't too involved).
  34.  */
  35.  
  36. static void Init()
  37. {
  38.     int i;
  39.  
  40.     rgppsiIns[128] = NULL; /** Make sure sample-info arrays are clear **/
  41.     for (i = 128; i--; i>=0) {
  42.         rgpsiDrum[i] = NULL;
  43.         rgppsiIns[i] = NULL;
  44.     }
  45. }
  46.  
  47. /*
  48.  * BuildFn: Builds a full filename given a string which is the old filename,
  49.  * and a default extension to use if one is not present in the string. After
  50.  * building the new filename, any extension in the old name is removed.
  51.  */
  52. void BuildFn(Fn fnNew, Sz fnOld, Sz fnExt)
  53. {
  54.     Sz fnT = fnNew;
  55.     int fExt = 0;
  56.  
  57.     while (*fnOld) {
  58.         if ('.' == (*(fnT++) = *fnOld)) { /** Copy a char, test for extension **/
  59.             fExt = 1;
  60.             *fnOld = 0; /** yes.. note extension exists and remove it **/
  61.         }
  62.         fnOld++;
  63.     }
  64.     if (!fExt) { /** If no extension found **/
  65.         *(fnT++) = '.';
  66.         while ((*(fnT++) = *(fnExt++))); /** copy the default one - fnExt **/
  67.     } else
  68.         *fnT = 0;
  69. }
  70.  
  71. /*
  72.  * SzReadPfile: Reads the next string from the textfile given and returns it.
  73.  * If file is at end, returns NULL.
  74.  */
  75. Sz SzReadPfile(FILE *pfileText)
  76. {
  77.     int ch, cch = 0, cchMac = 80;
  78.     Sz szT, szStart;
  79.  
  80.     if (feof(pfileText))
  81.         return NULL;
  82.     szStart = szT = (Sz) malloc(80); /** Set aside 80 characters for line **/
  83.     while ((ch = getc(pfileText)) != EOF && ch != '\n') {
  84.         *szT = ch;
  85.         if (++cch == cchMac) { /** If that's not enough **/
  86.             cchMac += 40;
  87.             szStart = (Sz) realloc(szStart, cchMac); /** increase in steps of 40 **/
  88.             szT = szStart + cch;
  89.         } else
  90.             szT++;
  91.     }
  92.     *szT = 0;
  93.     return (Sz) realloc(szStart, cch + 1);
  94. }
  95.  
  96. /*
  97.  * PsiAddsample: Given a sample's filename, will look it up in the tree
  98.  * and return a pointer to it if it exists, else will create it and return
  99.  * a pointer to the newly created entry.
  100.  */
  101. SI *PsiAddsample(Sz fnSample)
  102. {
  103.     SI *psiT;
  104.  
  105.     if (NULL == psiTree) { /** If nothing in tree **/
  106.         psiT = psiTree = (SI *) malloc(sizeof(SI)); /** create root node **/
  107.         psiT->fnSample = strdup(fnSample);
  108.         psiT->pitch = -1;
  109.         psiT->sample = -1;
  110.         psiT->psiL = psiT->psiR = NULL;
  111.     } else { /** Else **/
  112.         SI *psiOld;
  113.         int cmp;
  114.  
  115.         psiT = psiTree;
  116.         while (psiT != NULL) { /** find spot for sample in tree **/
  117.             psiOld = psiT;
  118.             cmp = strcmp(psiT->fnSample, fnSample);
  119.             if (!cmp)
  120.                 break;
  121.             else if (0 > cmp)
  122.                 psiT = psiT->psiL;
  123.             else
  124.                 psiT = psiT->psiR;
  125.         }
  126.         if (NULL == psiT) {
  127.             psiT = (SI *) malloc(sizeof(SI)); /** and create entry **/
  128.             if (0 > cmp)
  129.                 psiOld->psiL = psiT;
  130.             else
  131.                 psiOld->psiR = psiT;
  132.             psiT->fnSample = strdup(fnSample);
  133.             psiT->pitch = -1;
  134.             psiT->sample = -1;
  135.             psiT->psiL = psiT->psiR = NULL;
  136.         }
  137.     }
  138.     return psiT;
  139. }
  140.  
  141. /*
  142.  * PsiPrunePsi: Returns the given sample tree, but without any redundant
  143.  * samples. Any redundant samples are freed. If no samples remain, NULL
  144.  * is returned.
  145.  */
  146. SI *PsiPrunePsi(SI *psi)
  147. {
  148.     if (NULL == psi)
  149.         return NULL;
  150.     psi->psiL = PsiPrunePsi(psi->psiL); /** Prune left and right branches **/
  151.     psi->psiR = PsiPrunePsi(psi->psiR);
  152.     if (-1 == psi->pitch) { /** If root of tree redundant, need to remove it **/
  153.         SI *psiT;
  154.  
  155.         if (NULL == psi->psiL) { /** If no left branch **/
  156.             psiT = psi->psiR;
  157.             free(psi); /** replace root **/
  158.             psi = psiT; /** with right branch **/
  159.         } else if (NULL == psi->psiR) { /** If no right branch **/
  160.             psiT = psi->psiL;
  161.             free(psi); /** replace root **/
  162.             psi = psiT; /** with left branch **/
  163.         } else if (NULL == psi->psiL->psiR) { /** If left branch has no right **/
  164.             psiT = psi->psiL;
  165.             psiT->psiR = psi->psiR; /** put right branch on right of left **/
  166.             free(psi); /** and replace root **/
  167.             psi = psiT; /** with left branch **/
  168.         } else { /** Else.. there's 2 full branches - yuck! **/
  169.             SI *psiOld;
  170.  
  171.             psiT = psi->psiL;
  172.             while (NULL != psiT->psiR) { /** Find rightmost entry on left branch **/
  173.                 psiOld = psiT;
  174.                 psiT = psiT->psiR;
  175.             }
  176.             psiOld->psiR = psiT->psiL;
  177.             psiT->psiL = psi->psiL;
  178.             psiT->psiR = psi->psiR;
  179.             free(psi); /** remove root **/
  180.             psi = psiT; /** and replace it with that entry **/
  181.         }
  182.     }
  183.     return psi;
  184. }
  185.  
  186. /*
  187.  * ReadconfigFn: Given the filename of the configuration file, it interprets
  188.  * each line and sets up options and sample-tables.
  189.  */
  190. void ReadconfigFn(Sz fnConfig)
  191. {
  192.     FILE *pfileConfig;
  193.     Sz szLine, szTok;
  194.     int csz = 0, fError = 0;
  195.  
  196.     /* BJS on s:ptmid.cfg */
  197.     if(NULL==(pfileConfig=fopen("s:ptmid.cfg","rt"))) {
  198.         if (NULL == (pfileConfig = fopen(fnConfig, "rt"))) {
  199.             fprintf(stderr, "ptmid: Cannot find config file: %s\n", fnConfig);
  200.             exit(1);
  201.         }
  202.     }
  203.  
  204.     while ((szLine = SzReadPfile(pfileConfig)) != NULL) { /** With every line.. **/
  205.         csz++;
  206.         if ('#' != szLine[0] && NULL != (szTok = strtok(szLine, " \t")))
  207.             if ('0' <= szTok[0] && '9' >= szTok[0] || !strcmp(szTok, "def")) {
  208.                 int irgppsi, cpsi; /** If an instrument definition **/
  209.                 SI **ppsi;
  210.  
  211.                 if ('d' == szTok[0])
  212.                     irgppsi = 128;
  213.                 else
  214.                     irgppsi = atoi(szTok); /** decode instrument **/
  215.                 if (irgppsi < 129)
  216.                     while (NULL != (szTok = strtok(NULL, " \t"))) { /*** With every sample.. ***/
  217.                         if (NULL == rgppsiIns[irgppsi]) /*** Ensure allocated ***/
  218.                             rgppsiIns[irgppsi] = ppsi = (SI **) malloc(sizeof(SI *) * 2);
  219.                         else {
  220.                             ppsi = rgppsiIns[irgppsi];
  221.                             for (cpsi = 2; NULL != *ppsi; cpsi++, ppsi++);
  222.                             rgppsiIns[irgppsi] = ppsi = (SI **) realloc(rgppsiIns[irgppsi],
  223.                                 sizeof(SI *) * cpsi);
  224.                             ppsi += cpsi - 2;
  225.                         }
  226.                         ppsi[0] = PsiAddsample(szTok); /*** Put sample in array ***/
  227.                         ppsi[1] = NULL;
  228.                     }
  229.                 else
  230.                     fError = 1;
  231.  
  232.             } else if ('d' == szTok[0] && '0' <= szTok[1] && '9' >= szTok[1]) {
  233.                 int irgpsi; /** If a percussion definition **/
  234.  
  235.                 if ((irgpsi = atoi(szTok + 1)) < 128 && /** decode instrument **/
  236.                  (szTok = strtok(NULL, " \t")) != NULL) {
  237.                     if (NULL != rgpsiDrum[irgpsi])
  238.                         free(rgpsiDrum[irgpsi]); /** and free up if previously used **/
  239.                     rgpsiDrum[irgpsi] = PsiAddsample(szTok); /** Put sample in array **/
  240.                 } else
  241.                     fError = 1;
  242.  
  243.             } else if (!strcmp(szTok, "sample")) { /** If sample info **/
  244.                 Sz fnSample;
  245.                 SI *psi;
  246.                 int irgb;
  247.                 static int rgbPitch[7] = {45, 47, 36, 38, 40, 41, 43};
  248.  
  249.                 if ((fnSample = strtok(NULL, " \t")) == NULL) /** Get name **/
  250.                     fError = 1;
  251.                 else if ((szTok = strtok(NULL, " \t")) == NULL)
  252.                     fError = 1;
  253.                 else if ((irgb = toupper(szTok[0]) - 'A') < 0 || 6 < irgb) /** Get pitch **/
  254.                     fError = 1;
  255.                 else {
  256.                     psi = PsiAddsample(fnSample); /** Make sure sample allocated **/
  257.                     if ('#' == szTok[1])
  258.                         szTok++;
  259.                     psi->pitch = rgbPitch[irgb] + 12 * atoi(szTok + 1) +
  260.                         ('#' == szTok[0] ? 1 : 0);
  261.                     if ((szTok = strtok(NULL, " \t")) == NULL)
  262.                         psi->wLppos = 0;
  263.                     else
  264.                         psi->wLppos = atoi(szTok); /** Get loop start **/
  265.                     if ((szTok = strtok(NULL, " \t")) == NULL)
  266.                         psi->wLplen = 0;
  267.                     else
  268.                         psi->wLplen = atoi(szTok); /** Get loop length **/
  269.                 }
  270.  
  271.             } else if (!strcmp(szTok, "formid")) /** If module-id **/
  272.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  273.                     fError = 1;
  274.                 else
  275.                     strncpy(szId, szTok, 4); /** store it **/
  276.  
  277.             else if (!strcmp(szTok, "patmax")) /** If max. patterns **/
  278.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  279.                     fError = 1;
  280.                 else {
  281.                     int wT;
  282.  
  283.                     if ((wT = atoi(szTok)) != 0 && 128 >= wT)
  284.                         wPatmax = wT; /** store them **/
  285.                     else
  286.                         fError = 1;
  287.                 }
  288.  
  289.             else if (!strcmp(szTok, "ptchan")) /** If max. channels **/
  290.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  291.                     fError = 1;
  292.                 else {
  293.                     int wT;
  294.  
  295.                     if ((wT = atoi(szTok)) != 0 && MAXPTCHAN >= wT)
  296.                         wPtchan = wT; /** store them **/
  297.                     else
  298.                         fError = 1;
  299.                 }
  300.  
  301.             else if (!strcmp(szTok, "drumch")) /** If MIDI percussion channel **/
  302.                 if ((szTok = strtok(NULL, " \t")) == NULL)
  303.                     fError = 1;
  304.                 else {
  305.                     int bT;
  306.  
  307.                     if ((bT = atoi(szTok)) != 0)
  308.                         bDrumch = bT - 1; /** store it **/
  309.                 }
  310.  
  311.             else if (!strcmp(szTok, "fract") && NULL == szQuant) { /** If quantize frac. **/
  312.                 int wT;
  313.  
  314.                 if ((szQuant = strtok(NULL, " \t")) == NULL ||
  315.                  !(wT = ValidquantSz(strlwr(szQuant)))) /** decode **/
  316.                     fError = 1;
  317.                 else
  318.                     wQuantval = wT; /** and store it **/
  319.  
  320.             } else if (!strcmp(szTok, "extend")) /** If extend-notes flag **/
  321.                 fExtend = 1; /** toggle **/
  322.             else if (!strcmp(szTok, "nocopy")) /** If no-copyright flag **/
  323.                 fNocopy = 1; /** toggle **/
  324.             else
  325.                 fError = 1;
  326.  
  327.             if (fError) { /** If an error at any point, reveal line **/
  328.                 fprintf(stderr, "ptmid: Error in config file: line %d\n", csz);
  329.                 exit(1); /** and quit **/
  330.             }
  331.         free(szLine);
  332.     }
  333.     if (NULL == rgppsiIns[128]) {
  334.         fprintf(stderr, "ptmid: No default instrument defined in config file\n");
  335.         exit(1);
  336.     }
  337.     if ((psiTree = PsiPrunePsi(psiTree)) == NULL) {
  338.         fprintf(stderr, "ptmid: No sample definitions found in config file\n");
  339.         exit(1);
  340.     }
  341. }
  342.  
  343. /*
  344.  * main: Parses arguments to program and opens appropriate MOD and MID files.
  345.  */
  346. int main(int argc, char **argv)
  347. {
  348.     int cNames = 0;
  349.     Sz fnDef, fnConfig = DEFCONFIG;
  350.     Fn fnIn, fnOut;
  351.     FILE *pfileMod;
  352.     Tune *ptuneMusic;
  353.  
  354.     Init();
  355.     for (argv++; NULL != *argv; argv++) /** Run through all parameters **/
  356.         if ('-' == **argv) /** If parameter is a switch **/
  357.             switch ((*argv)[1]) { /** check what sort **/
  358.                 case 'c':
  359.                     if ((*argv)[2])
  360.                         fnConfig = *argv + 2; /** c: set config file **/
  361.                     break;
  362.                 case 'd':
  363.                     bDrumch = atoi(*argv + 2) - 1; /** d: set drum channel **/
  364.                     break;
  365.                 case 'f': {
  366.                     int wT; /** f: set quantize fraction **/
  367.  
  368.                     if ((wT = ValidquantSz(strlwr(*argv + 2))))
  369.                         wQuantval = wT;
  370.                     else
  371.                         fprintf(stderr, "ptmid: Invalid quantize fraction - using default\n");
  372.                     break;
  373.                 }
  374.                 case 'q':
  375.                     fQuiet = !fQuiet; /** q: toggle quiet mode **/
  376.                     break;
  377.             }
  378.         else { /** Else must be a filename **/
  379.             if (0 == cNames)
  380.                 BuildFn(fnIn, fnDef = *argv, "mid");
  381.             else if (1 == cNames)
  382.                 BuildFn(fnOut, *argv, "mod");
  383.             cNames++;
  384.         }
  385.  
  386.     if (1 > cNames || 2 < cNames) { /** If no filenames - error **/
  387.         printf("Use: ptmid [-cFile] [-dChannel] [-fFrac] [-q] infile[.mid] [outfile[.mod]]\n");
  388.         printf("Amiga V1.0 conversion by Brian Sullivan\n");
  389.         printf("  version " PTVER "\n");
  390.         printf("  Creates Protracker MOD files from General MIDI files\n");
  391.         exit(1);
  392.     } else if (1 == cNames) /** If one filename **/
  393.         BuildFn(fnOut, fnDef, "mod"); /** build other one **/
  394.     if (!fQuiet)
  395.         printf("ptmid ver " PTVER ": Converting '%s' to '%s'\n", fnIn, fnOut);
  396.  
  397.     ReadconfigFn(fnConfig);
  398.     if (access(fnIn, 1)) {
  399.         fprintf(stderr, "ptmid: Cannot access file: %s\n", fnIn);
  400.         exit(1);
  401.     }
  402.     if ((ptuneMusic = PtuneLoadFn(fnIn)) == NULL) {
  403.         fprintf(stderr, "ptmid: Not a legal MIDI file: %s\n", fnIn);
  404.         exit(1);
  405.     }
  406.  
  407.     if (!fQuiet)
  408.         printf("Analyzing..\n");
  409.     ResolvePtune(ptuneMusic);
  410.  
  411.     if (!fQuiet)
  412.         printf("Writing..\n");
  413.     if ((pfileMod = fopen(fnOut, "wb")) == NULL) {
  414.         fprintf(stderr, "ptmid: Cannot create file: %s\n", fnOut);
  415.         exit(1);
  416.     }
  417.     SavePtunePfile(ptuneMusic, pfileMod);
  418.     fclose(pfileMod);
  419.  
  420.     if (!fQuiet)
  421.         printf("Done.\n");
  422. }